今天也是非同步資料請求的主題,複習一下在XHR(XMLHttpRequest)搭配Promise的用法!
如果一個function被作為參數傳進另一個function,這種行為稱為"Callback",
而作為參數的function 則稱作"callback function"。
來看個例子:
doSomething(function(result){console.log(result);}, failureCallback)
假設doSomething()
執行的是,回傳成功執行參數1(匿名函式),回傳失敗則執行參數2(failureCallback)。
被傳入doSomething()
作為參數的兩個function,就被稱作callback function。
但當我們在函式裡執行更多行為時,被嵌套的callback會越來越多層,然後程式碼會變成這樣的結構:
function failureCallback() {
console.log("failed");
}
doSomething(function(result) {
doSecondThing(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
這種 Callback被一層層嵌套,導致 程式碼難以閱讀和維護 的情況,就被稱作 Callback Hell。*
或是例子不夠生動,可以看看網路上的圖片:
而為了避免產生callback Hell,現在多以使用promise的方式取代。
Promise 物件代表一個即將完成、或失敗的非同步操作,以及它所產生的值。
引用自Promise - JavaScript | MDN
意思是, Promise 代表 在進行非同步請求時,成功取得的資料 或 失敗的物件。
每當一個host要連結資料庫(database management system),資料庫應該回傳一個Promise。
Promise 的建構式語法:
new Promise( /* executor */ function(resolve, reject) { ... } );
new是指產生一個新物件,所以這裡會產生一個Promise的空物件,Promise 裡面包含 resolve 或 reject 兩種 callback function:
resolve
為成功取得的資料時,回傳資料。reject
則會在失敗時回傳 error object。每當傳入這兩個參數,executor函式就會直接執行,並在成功時執行resolve
,在失敗時執行reject
。
而promise很常被搭配使用兩個function:
.then()
接住 resolve 的結果。.catch()
接住 reject 的結果。舉例:
// 設定回傳resolve的情況
let example = new Promise((resolve, reject) => {
resolve({type: "object"});
});
example.then((data)=>{console.log(data);});
在這個範例裡,我們設定resolve
會回傳一個object。
並且在執行.then()
後,把資料印出來。
// 設定回傳reject的情況
let example = new Promise((resolve, reject) => {
reject(new Error("not allowed"));
});
example
.then((data)=>{console.log(data);})
.catch((error) => {console.log(error);});
而這個範例,是設定在reject
時,產生一個error object。
並在.catch()
抓到錯誤後,印出錯誤訊息。
promise的function是可以被串接起來的,被稱為 Promise Chain。
let example = new Promise((resolve, reject) => {
resolve({ string : "Hello" });
});
example
.then((data)=>{console.log(data);})
.then(() => {console.log("HI");})
.catch((error) => {console.log(error);});
// output: { string : "Hello" } ; "HI"
這樣的好處是,不管接續執行多少工作(.then()
),最後都只要指定一次失敗的情況(.catch()
)。
而不必像早期的方式(Callback Hell的範例),傳入許多次failureCallback
。
看完範例,最後來嘗試實際應用Promise。
延續昨天顯示氣溫的例子,今天試著用Promise改寫內容。
附上程式碼(和原來寫法的差異,寫在註解裡):
const section = document.querySelector('section');
// 獲得URL; 請記得將授權碼字樣替換掉!
var requestURL = 'https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-063?Authorization=授權碼&limit=1&offset=7&format=JSON&elementName=T';
function loadData(url){
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest();
request.open('GET', requestURL);
request.responseType = 'json';
request.onload = function() {
// 如果正確獲得資料,將資料寫進resolve
if (request.status === 200) {
resolve(request.response);
} else {
// 如果失敗,回傳錯誤訊息
reject(Error('error code:' + request.statusText));
}
}
request.send();
});
}
// promise chain
loadData(requestURL)
.then((response)=>showWeather(response))
.catch((error)=>console.log(error));
function showWeather(jsonObj) {
var jsonT = jsonObj['records']['locations'][0]['location'][0]['weatherElement'][0]['time'][0]['elementValue'][0]['value'];
const temperature = document.createElement('h1');
temperature.textContent = "地點: 台北市, 現在氣溫: " + jsonT + ' °C ';
section.appendChild(temperature);
}
運行結果:
附上一些和Promise相關的問題:
你對 Promises 的經驗?有用過相關的補強(ployfills)嗎?
可以參閱這篇文章:[JavaScript] 解決在 IE 11 出現 'Promise' 未經定義的錯誤,言簡意賅。
Promises 之於 callbacks 的優劣?
(待補充,優點上面有提到,缺點我要再找找資料。)
【如內文有誤還請不吝指教>< 謝謝閱覽至此的各位:D】
-----正文結束-----